#include "LabyrinthTest.h"
#include "Labyrinth.h"
#include "Position.h"
#include "Timeout.h"

CPPUNIT_TEST_SUITE_REGISTRATION(LabyrinthTest);

void LabyrinthTest::setUp() {
	START_TIMEOUT(1000);
}

void LabyrinthTest::tearDown() {
	STOP_TIMEOUT();
}

void LabyrinthTest::testPositionOnEdge() {
	Labyrinth labyrinth;
	CPPUNIT_ASSERT(labyrinth.isOnEdge(Position(1, Labyrinth::HEIGHT - 1)));
}

void LabyrinthTest::testPositionNotOnEdge() {
	Labyrinth labyrinth;
	CPPUNIT_ASSERT(! labyrinth.isOnEdge(Position(1, 1)));
}

void LabyrinthTest::testOutputOperator() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	ostringstream actual;
	actual << labyrinth;

	ostringstream expected;
	expected << "X X X X X X X " << endl
			<< "X - - - X - - " << endl
			<< "X - X - X - X " << endl
			<< "X - X - X - X " << endl
			<< "X - X X X - X " << endl
			<< "X - - - - - X " << endl
			<< "X X X X X X X " << endl;

	CPPUNIT_ASSERT_EQUAL(expected.str(), actual.str());
}

void LabyrinthTest::testGetCell() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT(labyrinth.getCell(Position(0, 0)) == Cell('X'));
	CPPUNIT_ASSERT(labyrinth.getCell(Position(1, 2)) == Cell('-'));
}

void LabyrinthTest::testSetCell() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	labyrinth.setCell(Position(0, 0), Cell::PLAYER);
	CPPUNIT_ASSERT(labyrinth.getCell(Position(0, 0)) == Cell::PLAYER);
}

void LabyrinthTest::testGetCellThrowsOutOfRangeExceptionWhenXLessThan0() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.getCell(Position(-1, 0)), out_of_range);
}

void LabyrinthTest::testGetCellThrowsOutOfRangeExceptionWhenXGreaterThanWIDTH() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.getCell(Position(7, 0)), out_of_range);
}

void LabyrinthTest::testGetCellThrowsOutOfRangeExceptionWhenYLessThan0() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.getCell(Position(0, -1)), out_of_range);
}

void LabyrinthTest::testGetCellThrowsOutOfRangeExceptionWhenYGreaterThanHEIGHT() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.getCell(Position(0, 7)), out_of_range);
}

void LabyrinthTest::testSetCellThrowsOutOfRangeExceptionWhenXLessThan0() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.setCell(Position(-1, 0), Cell::FREE), out_of_range);
}

void LabyrinthTest::testSetCellThrowsOutOfRangeExceptionWhenXGreaterThanWIDTH() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.setCell(Position(8, 0), Cell::FREE), out_of_range);
}

void LabyrinthTest::testSetCellThrowsOutOfRangeExceptionWhenYLessThan0() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.setCell(Position(0, -1), Cell::FREE), out_of_range);
}

void LabyrinthTest::testSetCellThrowsOutOfRangeExceptionWhenYGreaterThanHEIGHT() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.setCell(Position(0, 8), Cell::FREE), out_of_range);
}

void LabyrinthTest::testSetCellThrowsInvalidArgumentException() {
	const char testLabyrinth[Labyrinth::HEIGHT][Labyrinth::WIDTH] = {
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' },
		{ 'X', '-', '-', '-', 'X', '-', '-' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', '-', 'X', '-', 'X' },
		{ 'X', '-', 'X', 'X', 'X', '-', 'X' },
		{ 'X', '-', '-', '-', '-', '-', 'X' },
		{ 'X', 'X', 'X', 'X', 'X', 'X', 'X' }
	};
	Labyrinth labyrinth(testLabyrinth);
	CPPUNIT_ASSERT_THROW(labyrinth.setCell(Position(0, 0), Cell('Y')), invalid_argument);
}
